{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7e31ebc8-b449-4052-b959-8f473b6c9fb7",
   "metadata": {},
   "source": [
    "[![下载Notebook](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3/resource/_static/logo_notebook.svg)](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/r2.3/tutorials/zh_cn/beginner/mindspore_transforms.ipynb)&emsp;[![下载样例代码](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3/resource/_static/logo_download_code.svg)](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/r2.3/tutorials/zh_cn/beginner/mindspore_transforms.py)&emsp;[![查看源文件](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3/resource/_static/logo_source.svg)](https://gitee.com/mindspore/docs/blob/r2.3/tutorials/source_zh_cn/beginner/transforms.ipynb)\n",
    "\n",
    "[基本介绍](https://www.mindspore.cn/tutorials/zh-CN/r2.3/beginner/introduction.html) || [快速入门](https://www.mindspore.cn/tutorials/zh-CN/r2.3/beginner/quick_start.html) || [张量 Tensor](https://www.mindspore.cn/tutorials/zh-CN/r2.3/beginner/tensor.html) || [数据集 Dataset](https://www.mindspore.cn/tutorials/zh-CN/r2.3/beginner/dataset.html) || **数据变换 Transforms** || [网络构建](https://www.mindspore.cn/tutorials/zh-CN/r2.3/beginner/model.html) || [函数式自动微分](https://www.mindspore.cn/tutorials/zh-CN/r2.3/beginner/autograd.html) || [模型训练](https://www.mindspore.cn/tutorials/zh-CN/r2.3/beginner/train.html) || [保存与加载](https://www.mindspore.cn/tutorials/zh-CN/r2.3/beginner/save_load.html) || [使用静态图加速](https://www.mindspore.cn/tutorials/zh-CN/r2.3/beginner/accelerate_with_static_graph.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca3f817f-57c1-42df-9529-3e390d067cf1",
   "metadata": {},
   "source": [
    "# 数据变换 Transforms\n",
    "\n",
    "通常情况下，直接加载的原始数据并不能直接送入神经网络进行训练，此时我们需要对其进行数据预处理。MindSpore提供不同种类的数据变换（Transforms），配合数据处理Pipeline来实现数据预处理。所有的Transforms均可通过`map`方法传入，实现对指定数据列的处理。\n",
    "\n",
    "`mindspore.dataset`提供了面向图像、文本、音频等不同数据类型的Transforms，同时也支持使用Lambda函数。下面分别对其进行介绍。"
   ]
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "%%capture captured_output\n",
    "# 实验环境已经预装了mindspore==2.3.0，如需更换mindspore版本，可更改下面 MINDSPORE_VERSION 变量\n",
    "!pip uninstall mindspore -y\n",
    "!export MINDSPORE_VERSION=2.3.0\n",
    "!pip install https://ms-release.obs.cn-north-4.myhuaweicloud.com/${MINDSPORE_VERSION}/MindSpore/unified/aarch64/mindspore-${MINDSPORE_VERSION}-cp39-cp39-linux_aarch64.whl --trusted-host ms-release.obs.cn-north-4.myhuaweicloud.com -i https://pypi.mirrors.ustc.edu.cn/simple"
   ],
   "id": "9c2c09c092a913cb"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 查看当前 mindspore 版本\n",
    "!pip show mindspore"
   ],
   "id": "83c4494370400b22"
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "20a2f211-4515-4442-8c2b-c02b64dc668e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from PIL import Image\n",
    "from download import download\n",
    "from mindspore.dataset import transforms, vision, text\n",
    "from mindspore.dataset import GeneratorDataset, MnistDataset"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "761ddc32-ab75-41ee-91d6-f4a4709be632",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Common Transforms\n",
    "\n",
    "`mindspore.dataset.transforms`模块支持一系列通用Transforms。这里我们以`Compose`为例，介绍其使用方式。\n",
    "\n",
    "### Compose\n",
    "\n",
    "`Compose`接收一个数据增强操作序列，然后将其组合成单个数据增强操作。我们仍基于Mnist数据集呈现Transforms的应用效果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "abf6b9fe-688a-4dac-9a00-962eda1eb64f",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Downloading data from https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/MNIST_Data.zip (10.3 MB)\n",
      "\n",
      "file_sizes: 100%|██████████████████████████| 10.8M/10.8M [00:01<00:00, 9.01MB/s]\n",
      "Extracting zip file...\n",
      "Successfully downloaded / unzipped to ./\n"
     ]
    }
   ],
   "source": [
    "# Download data from open datasets\n",
    "\n",
    "url = \"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/\" \\\n",
    "      \"notebook/datasets/MNIST_Data.zip\"\n",
    "path = download(url, \"./\", kind=\"zip\", replace=True)\n",
    "\n",
    "train_dataset = MnistDataset('MNIST_Data/train')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1bab3ebe-d258-4581-ad69-9bb85a7b7814",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "(28, 28, 1)\n"
     ]
    }
   ],
   "source": [
    "image, label = next(train_dataset.create_tuple_iterator())\n",
    "print(image.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "87f4777b-1794-43b9-9816-44360b9c51db",
   "metadata": {},
   "outputs": [],
   "source": [
    "composed = transforms.Compose(\n",
    "    [\n",
    "        vision.Rescale(1.0 / 255.0, 0),\n",
    "        vision.Normalize(mean=(0.1307,), std=(0.3081,)),\n",
    "        vision.HWC2CHW()\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0adfef6c-d730-4f0c-9a53-720cfc0d61c0",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "(1, 28, 28)\n"
     ]
    }
   ],
   "source": [
    "train_dataset = train_dataset.map(composed, 'image')\n",
    "image, label = next(train_dataset.create_tuple_iterator())\n",
    "print(image.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "750f876f-8263-4ea6-b085-5d3275f3e311",
   "metadata": {},
   "source": [
    "更多通用Transforms详见[mindspore.dataset.transforms](https://www.mindspore.cn/docs/zh-CN/r2.3/api_python/mindspore.dataset.transforms.html)。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23afcc06-4bfd-4a57-97e2-b5c09e05b142",
   "metadata": {},
   "source": [
    "## Vision Transforms\n",
    "\n",
    "`mindspore.dataset.vision`模块提供一系列针对图像数据的Transforms。在Mnist数据处理过程中，使用了`Rescale`、`Normalize`和`HWC2CHW`变换。下面对其进行详述。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cda18d2-7ad0-47c9-970a-ed5900c4ee3b",
   "metadata": {},
   "source": [
    "### Rescale\n",
    "\n",
    "`Rescale`变换用于调整图像像素值的大小，包括两个参数：\n",
    "\n",
    "- rescale：缩放因子。\n",
    "- shift：平移因子。\n",
    "\n",
    "图像的每个像素将根据这两个参数进行调整，输出的像素值为$output_{i} = input_{i} * rescale + shift$。\n",
    "\n",
    "这里我们先使用numpy随机生成一个像素值在\\[0, 255\\]的图像，将其像素值进行缩放。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c99377ed-e1ae-4c6f-8285-cde61039c069",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "[[170  10 218 ...  81 128  96]\n [  2 107 146 ... 239 178 165]\n [232 137 235 ... 222 109 216]\n ...\n [193 140  60 ...  72 133 144]\n [232 175  58 ...  55 110  94]\n [152 241 105 ... 187  45  43]]\n"
     ]
    }
   ],
   "source": [
    "random_np = np.random.randint(0, 255, (48, 48), np.uint8)\n",
    "random_image = Image.fromarray(random_np)\n",
    "print(random_np)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b17ac44-14ec-48a4-a43a-da4de1ad5c3e",
   "metadata": {},
   "source": [
    "为了更直观地呈现Transform前后的数据对比，我们使用Transforms的[Eager模式](https://www.mindspore.cn/tutorials/zh-CN/r2.3/advanced/dataset/eager.html)进行演示。首先实例化Transform对象，然后调用对象进行数据处理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "516f4169-a13f-42b2-b976-222b029194a1",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "[[0.6666667  0.03921569 0.854902   ... 0.31764707 0.5019608  0.37647063]\n [0.00784314 0.41960788 0.57254905 ... 0.93725497 0.69803923 0.64705884]\n [0.909804   0.5372549  0.9215687  ... 0.8705883  0.427451   0.8470589 ]\n ...\n [0.7568628  0.54901963 0.23529413 ... 0.28235295 0.52156866 0.5647059 ]\n [0.909804   0.6862745  0.227451   ... 0.21568629 0.43137258 0.36862746]\n [0.59607846 0.9450981  0.41176474 ... 0.73333335 0.1764706  0.16862746]]\n"
     ]
    }
   ],
   "source": [
    "rescale = vision.Rescale(1.0 / 255.0, 0)\n",
    "rescaled_image = rescale(random_image)\n",
    "print(rescaled_image)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3cb95c6d-3db1-486f-b302-e97ab33c1d17",
   "metadata": {},
   "source": [
    "可以看到，使用`Rescale`后的每个像素值都进行了缩放。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3361af8a-08f7-45f5-92b6-1ad3889c82e8",
   "metadata": {},
   "source": [
    "### Normalize\n",
    "\n",
    "[Normalize](https://www.mindspore.cn/docs/zh-CN/r2.3/api_python/dataset_vision/mindspore.dataset.vision.Normalize.html)变换用于对输入图像的归一化，包括三个参数：\n",
    "\n",
    "- mean：图像每个通道的均值。\n",
    "- std：图像每个通道的标准差。\n",
    "- is_hwc：bool值，输入图像的格式。True为(height, width, channel)，False为(channel, height, width)。\n",
    "\n",
    "图像的每个通道将根据`mean`和`std`进行调整，计算公式为$output_{c} = \\frac{input_{c} - mean_{c}}{std_{c}}$，其中 $c$代表通道索引。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "e370e1a3-1101-4280-be6a-befb0fbb532d",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "[[ 1.7395868  -0.29693064  2.3505423  ...  0.60677403  1.2050011\n   0.7976976 ]\n [-0.3987565   0.9377082   1.4341093  ...  2.617835    1.8414128\n   1.6759458 ]\n [ 2.5287375   1.3195552   2.5669222  ...  2.4014552   0.9631647\n   2.3250859 ]\n ...\n [ 2.0323365   1.3577399   0.33948112 ...  0.49221992  1.2686423\n   1.4086528 ]\n [ 2.5287375   1.803228    0.31402466 ...  0.27583995  0.9758929\n   0.77224106]\n [ 1.5104787   2.6432917   0.9122518  ...  1.9559668   0.14855757\n   0.12310111]]\n"
     ]
    }
   ],
   "source": [
    "normalize = vision.Normalize(mean=(0.1307,), std=(0.3081,))\n",
    "normalized_image = normalize(rescaled_image)\n",
    "print(normalized_image)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdf32c52-9f7c-428a-97d3-3c207e4ad092",
   "metadata": {},
   "source": [
    "### HWC2CHW\n",
    "\n",
    "`HWC2CHW`变换用于转换图像格式。在不同的硬件设备中可能会对(height, width, channel)或(channel, height, width)两种不同格式有针对性优化。MindSpore设置HWC为默认图像格式，在有CHW格式需求时，可使用该变换进行处理。\n",
    "\n",
    "这里我们先将前文中`normalized_image`处理为HWC格式，然后进行转换。可以看到转换前后的shape发生了变化。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "3725b284-f2df-45f3-a7f7-8c3a68e3d44a",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "(48, 48, 1) (1, 48, 48)\n"
     ]
    }
   ],
   "source": [
    "hwc_image = np.expand_dims(normalized_image, -1)\n",
    "hwc2chw = vision.HWC2CHW()\n",
    "chw_image = hwc2chw(hwc_image)\n",
    "print(hwc_image.shape, chw_image.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43ab9fbc-3201-425b-a5f0-62696a5446a0",
   "metadata": {},
   "source": [
    "更多Vision Transforms详见[mindspore.dataset.vision](https://www.mindspore.cn/docs/zh-CN/r2.3/api_python/mindspore.dataset.transforms.html#视觉)。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1128625a-8bdc-4e90-8a2f-b951f4b27752",
   "metadata": {},
   "source": [
    "## Text Transforms"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a25654c-2850-4df0-9214-531c25f751af",
   "metadata": {},
   "source": [
    "`mindspore.dataset.text`模块提供一系列针对文本数据的Transforms。与图像数据不同，文本数据需要有分词（Tokenize）、构建词表、Token转Index等操作。这里简单介绍其使用方法。\n",
    "\n",
    "首先我们定义三段文本，作为待处理的数据，并使用`GeneratorDataset`进行加载。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "6d93837b-d975-4044-8e24-58fb2f4e5011",
   "metadata": {},
   "outputs": [],
   "source": [
    "texts = ['Welcome to Beijing']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "598bca9b-e755-4fe2-a7d6-b152e82c9d1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "test_dataset = GeneratorDataset(texts, 'text')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9ab6175-6b60-495e-bb4d-37b97052c11e",
   "metadata": {},
   "source": [
    "### PythonTokenizer\n",
    "\n",
    "分词（Tokenize）操作是文本数据的基础处理方法，MindSpore提供多种不同的Tokenizer。这里我们选择基础的`PythonTokenizer`举例，此Tokenizer允许用户自由实现分词策略。随后我们利用`map`操作将此分词器应用到输入的文本中，对其进行分词。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "6636e1d1-59b9-4198-bab9-5a7343a66710",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "[Tensor(shape=[3], dtype=String, value= ['Welcome', 'to', 'Beijing'])]\n"
     ]
    }
   ],
   "source": [
    "def my_tokenizer(content):\n",
    "    return content.split()\n",
    "\n",
    "test_dataset = test_dataset.map(text.PythonTokenizer(my_tokenizer))\n",
    "print(next(test_dataset.create_tuple_iterator()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7ed698c-1edc-4c56-8a24-498ade32b2b9",
   "metadata": {},
   "source": [
    "### Lookup\n",
    "\n",
    "`Lookup`为词表映射变换，用来将Token转换为Index。在使用`Lookup`前，需要构造词表，一般可以加载已有的词表，或使用`Vocab`生成词表。这里我们选择使用`Vocab.from_dataset`方法从数据集中生成词表。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "00884c33-a3ee-4b8f-8ee3-582d210fce85",
   "metadata": {},
   "outputs": [],
   "source": [
    "vocab = text.Vocab.from_dataset(test_dataset)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a7dbd0a-5c0f-49f6-b562-feb0ddabfba1",
   "metadata": {},
   "source": [
    "获得词表后我们可以使用`vocab`方法查看词表。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "cebc8d0d-a9e4-45c3-bc00-742d903632e9",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "{'to': 2, 'Beijing': 0, 'Welcome': 1}\n"
     ]
    }
   ],
   "source": [
    "print(vocab.vocab())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccde57b8-74f9-4ff2-915f-17bd1da87c59",
   "metadata": {},
   "source": [
    "生成词表后，可以配合`map`方法进行词表映射变换，将Token转为Index。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "8919313b-e1c8-45e0-8b81-e0815f75b626",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "[Tensor(shape=[3], dtype=Int32, value= [1, 2, 0])]\n"
     ]
    }
   ],
   "source": [
    "test_dataset = test_dataset.map(text.Lookup(vocab))\n",
    "print(next(test_dataset.create_tuple_iterator()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "425077b8-0a15-4317-9d44-16d0bc84ea4c",
   "metadata": {},
   "source": [
    "更多Text Transforms详见[mindspore.dataset.text](https://www.mindspore.cn/docs/zh-CN/r2.3/api_python/mindspore.dataset.transforms.html#文本)。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2103e098-7a4f-43da-af61-14640aeb66e7",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Lambda Transforms\n",
    "\n",
    "Lambda函数是一种不需要名字、由一个单独表达式组成的匿名函数，表达式会在调用时被求值。Lambda Transforms可以加载任意定义的Lambda函数，提供足够的灵活度。在这里，我们首先使用一个简单的Lambda函数，对输入数据乘2："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "f49555eb-82a2-449e-b97f-ce9620e9d7d4",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "[[Tensor(shape=[], dtype=Int64, value= 2)], [Tensor(shape=[], dtype=Int64, value= 4)], [Tensor(shape=[], dtype=Int64, value= 6)]]\n"
     ]
    }
   ],
   "source": [
    "test_dataset = GeneratorDataset([1, 2, 3], 'data', shuffle=False)\n",
    "test_dataset = test_dataset.map(lambda x: x * 2)\n",
    "print(list(test_dataset.create_tuple_iterator()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e8f538c-9b88-48aa-924e-e1023fbd2f5b",
   "metadata": {},
   "source": [
    "可以看到`map`传入Lambda函数后，迭代获得数据进行了乘2操作。\n",
    "\n",
    "我们也可以定义较复杂的函数，配合Lambda函数实现复杂数据处理："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "124f2f07-cc2f-4093-ba1f-9de56adcf06a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def func(x):\n",
    "    return x * x + 2\n",
    "\n",
    "test_dataset = test_dataset.map(lambda x: func(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "0deb59e5-54c3-4c12-9e00-f48570b9bb77",
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "[[Tensor(shape=[], dtype=Int64, value= 6)], [Tensor(shape=[], dtype=Int64, value= 18)], [Tensor(shape=[], dtype=Int64, value= 38)]]\n"
     ]
    }
   ],
   "source": [
    "print(list(test_dataset.create_tuple_iterator()))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "MindSpore",
   "language": "python",
   "name": "mindspore"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  },
  "vscode": {
   "interpreter": {
    "hash": "8c9da313289c39257cb28b126d2dadd33153d4da4d524f730c81a4aaccbd2ca7"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
